#pragma once

#include "segmap.h"

namespace outline {

template <typename T>
template <typename I>
segmap<T>::segmap(I first, I last)
{
	shw = 10;
	if (first == last)
		return;
	point_type o = *first;
	for (++first; first != last; ++first) {
		point_type t = *first;
		if (o.x == t.x) {
			//vertical segment
			if (o.y < t.y) {
				assert (o.y+1 == t.y);
				//from top to bottom, outer normal sees left
				vl.push_back(o);
			}
			else {
				assert (o.y == t.y+1);
				//from bottom to top, outer normal sees right
				vr.push_back(t);
			}
		}
		else {
			//horizontal segment
			assert (o.y == t.y);
			if (o.x < t.x) {
				assert (o.x+1 == t.x);
				//from left to right, outer normal sees down
				hd.push_back(o);
			}
			else {
				assert (o.x == t.x+1);
				//from right to left, outer normal sees up
				hu.push_back(t);
			}
		}
		o = t;
	}

	vl.rebalance();
	vr.rebalance();
	hu.rebalance();
	hd.rebalance();
}

template <typename T>
inline void segmap<T>::search_half_window(size_type h)
	{ shw = h; }

template <typename T>
inline typename segmap<T>::size_type segmap<T>::search_half_window() const
	{ return shw; }

template <typename T>
template <typename U> //U is some real type
point<U> segmap<T>::nearest(point<U> const & pos, point<U> const & norm) const
{
	point<U> result = pos;
	U distance = std::numeric_limits<U>::max();
	tree_type const * tree;
	bool xsegm;
	if (norm.x >= norm.y) {
		if (norm.x >= -norm.y) {
			//norm sees right
			tree = &vr;
			xsegm = false;
		}
		else {
			//norm sees up
			tree = &hu;
			xsegm = true;
		}
	}
	else {
		if (norm.x >= -norm.y) {
			//norm sees down
			tree = &hd;
			xsegm = true;
		}
		else {
			//norm sees left
			tree = &vl;
			xsegm = false;
		}
	}

	rect_type r(
		pos.x <= shw ? 0 : pos.x - shw,
		pos.y <= shw ? 0 : pos.y - shw,
		pos.x + shw + 1,
		pos.y + shw + 1);
	for (range_iterator it = tree->range_begin(r); it != tree->range_end(); ++it) {
		point_type const & f = *it;
		point<U> r = f;
		//increment of point found in tree to achive position of another end of the segment
		if (xsegm)
			r.x += U(0.5);
		else
			r.y += U(0.5);
		U xd = square(r - pos);
		if (xd < distance) {
			distance = xd;
			if (xsegm) {
				result.x = (pos.x <= f.x) ? f.x : ((pos.x >= f.x+1) ? f.x+1 : pos.x);
				result.y = f.y;
			}
			else {
				result.x = f.x;
				result.y = (pos.y <= f.y) ? f.y : ((pos.y >= f.y+1) ? f.y+1 : pos.y);
			}
		}
	}
	return result;
}

} //namespace outline
